#ifndef GWI_H_INCL
#define GWI_H_INCL

#define DDSQLEN 0x10

#include <windows.h>
#include "../external/ddraw.h"

struct GameWindowInfo
{
// Construction/destruction
public:
    GameWindowInfo();
    virtual ~GameWindowInfo();

// Operations
public: // methods
	//void InitWindowPos(int, int, int, int);
	void SetScreenSize(void) {dwScreenWidth=800; dwScreenHeight=600;}
	void SetScreenSize(int x, int y) {if(x)dwScreenWidth=x; if(y)dwScreenHeight=y;}
	BOOL IsDesktop(HWND);
	BOOL IsRealDesktop(HWND);
	POINT FixCursorPos(POINT);
	void FixNCHITCursorPos(LPPOINT);
	//void SetClipCursor(void);
	//void EraseClipCursor(void);
	//RECT MapWindowRect(LPRECT lpRect);
	//RECT MapWindowRect(void);
	void MapToWindow(LPPOINT);
	void MapToWindow(LPRECT);
	void MapToWindow(int *, int *, int *, int *);
	void MapToWindow(int *, int *);
	void UnmapFromWindow(LPPOINT);
	void UnmapFromWindow(int *, int *);
	void FixWorkarea(LPRECT);
	POINT FixMessagePt(HWND, POINT);
	//RECT GetScreenRect(void);
	//RECT GetWindowRect(RECT);
	//RECT GetClientRect(RECT);
	POINT AddCoordinates(POINT, POINT);
	POINT SubCoordinates(POINT, POINT);
	POINT ClientOffset(HWND);
	void ScreenRefresh(void);
	//DWORD GetTickCount(void);
	void MarkPrimarySurface(void*);
	BOOL IsAPrimarySurface(void*);
	void* GetPrimarySurface(void);
	void MarkBackBufferSurface(void*);
	BOOL IsABackBufferSurface(void*);
	void* GetBackBufferSurface(void);
	void MarkRegularSurface(void*);
	void SetPrimarySurface(void);
	void ResetPrimarySurface(void);
	//void GetSystemTime(LPSYSTEMTIME lpSystemTime);
	//void GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
	//DWORD StretchTime(DWORD);
	//DWORD StretchCounter(DWORD);
	//char *GetTSCaption(int);
	//char *GetTSCaption(void);
	//void DoSlow(int);
	//POINT ScreenToClient(POINT);
	//int GetDLLIndex(char *);
	//void FixStyle(char *, HWND, WPARAM, LPARAM);
	HDC AcquireEmulatedDC(HWND);
	HDC AcquireEmulatedDC(HDC);
	BOOL ReleaseEmulatedDC(HWND);
	BOOL IsVirtual(HDC);
	void ResetEmulatedDC();
	int VirtualOffsetX, VirtualOffsetY;
	void SetPalette(DWORD, DWORD, LPPALETTEENTRY);
	COLORREF GetMatchingColor(COLORREF);

public: // simple data variables
	HWND hWnd;
	BOOL bFullScreenGame;
	DWORD dwScreenWidth;
	DWORD dwScreenHeight;
	//DWORD dwPixelFormatBPP; // TODO: Merge into one of the existing pixel formats.
	DWORD dwDisplayRefresh;
	DDPIXELFORMAT ddpfActualPixelFormat;
	DDPIXELFORMAT ddpfVirtualPixelFormat;
	//DWORD dwDDVersion;
	//DWORD dwTargetDDVersion;
	//DWORD dwMaxDDVersion;
	HWND hChildWnd;
	BOOL bActive;
	BOOL bDInputAbs;
	char *gsModules;
	LPDIRECTDRAWSURFACE lpDDSPrimHDC;
	BOOL bEmulateDC;
	int originalPosX;
	int originalPosY;
	short iSizX;
	short iSizY;
	//BOOL IsWithinDDraw; // flag to avoid double intervention at ddraw & GDI level
	//BOOL IsGDIPalette;
	//char *CustomOpenGLLib;
	// Palette information
    WORD palVersion;
    WORD palNumEntries;
    PALETTEENTRY palPalette[256];
	DWORD dwPaletteEntries[256]; // Use COLORREF type instead?
	bool bIsPaletteUpdated;

	short FakeVersionId;
	int Coordinates;

// Implementation
protected:
	void* lpddsPrimarySurfaces[DDSQLEN+1];
	void* lpddsBackSurfaces[DDSQLEN+1];
	HDC VirtualHDC;
	HBITMAP VirtualPic;

private:
	void UnmarkPrimarySurface(LPDIRECTDRAWSURFACE);
	void UnmarkBackBufferSurface(LPDIRECTDRAWSURFACE);

};

/* ------------------------------------------------------------------ */
// Constructor, destructor, initialization....
/* ------------------------------------------------------------------ */

GameWindowInfo::GameWindowInfo()
{
	// initialization stuff ....
	fullScreenGame = false;
	hWnd = NULL;
	SetScreenSize();
	dwMaxDDVersion=7;
	//hParentWnd = 0;
	hChildWnd = 0;
	bActive = TRUE;
	bDInputAbs = 0;
	//TimeShift = 0;
	lpDDSPrimHDC = NULL;
	bEmulateDC = TRUE;
	//IsWithinDDraw = FALSE;
	//IsGDIPalette = FALSE;
	memset(PrimSurfaces, 0, sizeof(PrimSurfaces));
	ResetEmulatedDC();
}

GameWindowInfo::~GameWindowInfo()
{
}

/* ------------------------------------------------------------------ */
// Primary surfaces auxiliary functions
/* ------------------------------------------------------------------ */

void GameWindowInfo::UnmarkPrimarySurface(void* ps)
{
	int i;
	for(i = 0; i < DDSQLEN; i++)
	{
		if(lpddsPrimarySurfaces[i] == ps) break; 
		if(lpddsPrimarySurfaces[i] == 0) break;
	}
	if(lpddsPrimarySurfaces[i] == ps)
	{
		for(; i < DDSQLEN; i++)
		{
			lpddsPrimarySurfaces[i] = lpddsPrimarySurfaces[i+1];
			if(lpddsPrimarySurfaces[i] == 0) break;
		}
		lpddsPrimarySurfaces[DDSQLEN] = 0;
	}
}

void GameWindowInfo::UnmarkBackBufferSurface(void* ps)
{
	int i;
	for(i = 0; i < DDSQLEN; i++)
	{
		if(lpddsBackSurfaces[i] == ps) break; 
		if(lpddsBackSurfaces[i] == 0) break;
	}
	if(BackSurfaces[i]==(DWORD)ps)
	{
		for(; i < DDSQLEN; i++)
		{
			lpddsBackSurfaces[i] = lpddsBackSurfaces[i+1];
			if(lpddsBackSurfaces[i] == 0) break;
		}
		lpddsBackSurfaces[DDSQLEN] = 0;
	}
}

void GameWindowInfo::MarkPrimarySurface(void* ps)
{
	int i;
	for(i = 0; i < DDSQLEN; i++)
	{
		if(lpddsPrimarySurfaces[i] == ps) return; // if already there ....
		if(lpddsPrimarySurfaces[i] == 0) break; // got end of list
	}
	lpddsPrimarySurfaces[i] = ps;
	UnmarkBackBufferSurface(ps);
}

void GameWindowInfo::MarkBackBufferSurface(void* ps)
{
	for(int i = 0; i < DDSQLEN; i++)
	{
		if(lpddsBackSurfaces[i] == ps) return; // if already there ....
		if(lpddsBackSurfaces[i] == 0) break; // got end of list
	}
	lpddsBackSurfaces[i] = ps;
	UnmarkPrimarySurface(ps);
}

void GameWindowInfo::MarkRegularSurface(void* ps)
{
	UnmarkBackBufferSurface(ps);
	UnmarkPrimarySurface(ps);
}

// Note: since MS itself declares that the object refcount is not reliable and should
// be used for debugging only, it's not safe to rely on refcount==0 when releasing a
// surface to terminate its classification as primary. As an alternate and more reliable
// way, we use UnmarkPrimarySurface each time you create a new not primary surface, 
// eliminating the risk that a surface previously classified as primary and then closed
// had the same surface handle than this new one that is not primary at all.

BOOL GameWindowInfo::IsAPrimarySurface(void* ps)
{
	// treat NULL surface ptr as a non primary
	if(!ps) return FALSE;
	for(int i = 0; i < DDSQLEN; i++)
	{
		if(lpddsPrimarySurfaces[i] == 0) return FALSE;
		if(lpddsPrimarySurfaces[i] == ps) return TRUE;
	}
	return FALSE;
}

BOOL GameWindowInfo::IsABackBufferSurface(void* ps)
{
	// treat NULL surface ptr as a non primary
	if(!ps) return FALSE;
	for(int i = 0; i < DDSQLEN; i++)
	{
		if(lpddsBackSurfaces[i] == 0) return FALSE;
		if(lpddsBackSurfaces[i] == ps) return TRUE;
	}
	return FALSE;
}

void* GameWindowInfo::GetPrimarySurface(void)
{
	// return last opened one....
	int i;
	for (int i = 0; i < DDSQLEN; i++)
	{
		if(lpddsPrimarySurfaces[i] == 0)
		{
			if(i > 0) return lpddsPrimarySurfaces[i-1];
			break;
		}
	}
	return NULL;
}

void* GameWindowInfo::GetBackBufferSurface(void)
{
	// return last opened one....
	int i;
	for(int i = 0; i < DDSQLEN; i++)
	{
		if(lpddsBackSurfaces[i] == 0)
		{
			if(i > 0) return lpddsBackSurfaces[i-1];
			break;
		}
	}
	return NULL;
}

void GameWindowInfo::SetPrimarySurface(void)
{
	if (!lpDDSPrimHDC) lpDDSPrimHDC=GetPrimarySurface();
}

void GameWindowInfo::ResetPrimarySurface(void)
{
	lpDDSPrimHDC=NULL;
}

//void GameWindowInfo::InitWindowPos(int x, int y, int w, int h)
//{
//	iPosX = x;
//	iPosY = y; //v2.02.09
//	iSizX = w;
//	iSizY = h;
//}

//RECT GameWindowInfo::GetScreenRect()
//{
//	static RECT Screen;
//	Screen.left=0;
//	Screen.top=0;
//	Screen.right=dwScreenWidth;
//	Screen.bottom=dwScreenHeight;
//	return Screen;
//}

// TODO: Rename to something clearer
BOOL GameWindowInfo::IsDesktop(HWND hwnd)
{
	return ((hwnd == 0) || (hwnd == GetDesktopWindow()) || (hwnd == hWnd));
}

BOOL GameWindowInfo::IsRealDesktop(HWND hwnd)
{
	return ((hwnd == 0) || (hwnd == GetDesktopWindow()));
}

// v2.1.93: FixCursorPos completely revised to introduce a clipping tolerance in
// clipping regions as well as in normal operations

// TODO: Fix fullScreenGames vs not fullScreen, so that we don't prevent the mouse from reaching menus etc.
// Currently this is for full screen only.

#define CLIP_TOLERANCE 4

POINT GameWindowInfo::FixCursorPos(POINT prev)
{
	POINT curr;
	RECT rect;
	extern LPRECT lpClipRegion;
	extern CurrentInput previnput;

	curr=prev;

	// scale mouse coordinates
	// remember: rect from GetClientRect always start at 0,0!
	//if(gwi.dwFlags1 & MODIFYMOUSE){
		int w, h, b; // width, height and border
		int iRatioX, iRatioY;

		// ratio is the configured one, unless the values are 0. Then, it's the standard 4:3 ratio.
		iRatioX = iSizX ? iSizX : 4;
		iRatioY = iSizY ? iSizY : 3;

		if (!(GetClientRect(hWnd, &rect))) { // v2.02.30: always use desktop win
			//OutTraceD("GetClientRect ERROR %d at %d\n", GetLastError(),__LINE__);
			curr.x = curr.y = 0;
		}
		w = rect.right - rect.left;
		h = rect.bottom - rect.top;

		//if (gwi.dwFlags2 & KEEPASPECTRATIO) {
			if ((w * iRatioY) > (h * iRatioX)){
				b = (w - (h * iRatioX / iRatioY))/2;
				curr.x -= b;
				w -= 2*b;
			}
			else {
				b = (h - (w * iRatioY / iRatioX))/2;
				curr.y -= b;
				h -= 2*b;
			}
		//}

		// If we are outside the screen, replace our coordinates with the previous input			
		if (curr.x < 0 || curr.x > w) curr.x = previnput.mouse.coords.x;
		if (curr.y < 0 || curr.y > h) curr.y = previnput.mouse.coords.y;
		//if (curr.x > w) curr.x = w;
		//if (curr.y > h) curr.y = h;

		if (w) curr.x = (curr.x * gwi.dwScreenWidth) / w;
		if (h) curr.y = (curr.y * gwi.dwScreenHeight) / h;
	//}

	if(/*(gwi.dwFlags1 & ENABLECLIPPING) && */lpClipRegion){
		// v2.1.93:
		// in clipping mode, avoid the cursor position to lay outside the valid rect
		// note 1: the rect follow the convention and valid coord lay between left to righ-1,
		// top to bottom-1
		// note 2: CLIP_TOLERANCE is meant to handle possible integer divide tolerance errors
		// that may prevent reaching the clip rect borders. The smaller you shrink the window, 
		// the bigger tolerance is required
		if (curr.x < lpClipRegion->left+CLIP_TOLERANCE) curr.x=lpClipRegion->left;
		if (curr.y < lpClipRegion->top+CLIP_TOLERANCE) curr.y=lpClipRegion->top;
		if (curr.x >= lpClipRegion->right-CLIP_TOLERANCE) curr.x=lpClipRegion->right-1;
		if (curr.y >= lpClipRegion->bottom-CLIP_TOLERANCE) curr.y=lpClipRegion->bottom-1;
	}
	//else{
	//	if (curr.x < CLIP_TOLERANCE) curr.x=0;
	//	if (curr.y < CLIP_TOLERANCE) curr.y=0;
	//	if (curr.x >= (LONG)gwi.GetScreenWidth()-CLIP_TOLERANCE) curr.x=gwi.GetScreenWidth()-1;
	//	if (curr.y >= (LONG)gwi.GetScreenHeight()-CLIP_TOLERANCE) curr.y=gwi.GetScreenHeight()-1;
	//}

	return curr;
}

//POINT GameWindowInfo::ScreenToClient(POINT point)
//{
//	// convert absolute screen coordinates to frame relative
//	if (!ScreenToClient(hWnd, &point)) {
//		//OutTraceE("ScreenToClient(%x) ERROR %d at %d\n", hWnd, GetLastError(), __LINE__);
//		point.x =0; point.y=0;
//	}
//
//	return point;
//}

void GameWindowInfo::FixNCHITCursorPos(LPPOINT lppoint)
{
	RECT rect;
	POINT point;

	point=*lppoint;
	(*pGetClientRect)(gwi.hWnd, &rect);
	(*pScreenToClient)(gwi.hWnd, &point);

	if (point.x < 0) return;
	if (point.y < 0) return;
	if (point.x > rect.right) return;
	if (point.y > rect.bottom) return;

	*lppoint=point;
	lppoint->x = (lppoint->x * gwi.GetScreenWidth()) / rect.right;
	lppoint->y = (lppoint->y * gwi.GetScreenHeight()) / rect.bottom;
	if(lppoint->x < CLIP_TOLERANCE) lppoint->x=0;
	if(lppoint->y < CLIP_TOLERANCE) lppoint->y=0;
	if(lppoint->x > (LONG)gwi.GetScreenWidth()-CLIP_TOLERANCE) lppoint->x=gwi.GetScreenWidth()-1;
	if(lppoint->y > (LONG)gwi.GetScreenHeight()-CLIP_TOLERANCE) lppoint->y=gwi.GetScreenHeight()-1;
}

//void GameWindowInfo::SetClipCursor()
//{
//	RECT Rect;
//	POINT UpLeftCorner={0,0};
//
//	//OutTraceD("SetClipCursor:\n");
//	if (hWnd==NULL) {
//		//OutTraceD("SetClipCursor: ASSERT hWnd==NULL\n");
//		return;
//	}
//	// TODO: Real error management? Or should this "never" happen?
//	if(!(*pGetClientRect)(hWnd, &Rect))
//		//OutTraceE("GetClientRect: ERROR err=%d at %d\n", GetLastError(), __LINE__);
//	if(!(*pClientToScreen)(hWnd, &UpLeftCorner))
//		//OutTraceE("ClientToScreen: ERROR err=%d at %d\n", GetLastError(), __LINE__);
//	Rect.left+=UpLeftCorner.x;
//	Rect.right+=UpLeftCorner.x;
//	Rect.top+=UpLeftCorner.y;
//	Rect.bottom+=UpLeftCorner.y;
//	(*pClipCursor)(NULL);
//	if(!(*pClipCursor)(&Rect))
//		//OutTraceE("ClipCursor: ERROR err=%d at %d\n", GetLastError(), __LINE__);
//	//OutTraceD("SetClipCursor: rect=(%d,%d)-(%d,%d)\n",
//		//Rect.left, Rect.top, Rect.right, Rect.bottom);
//}

//void GameWindowInfo::EraseClipCursor()
//{
//	//OutTraceD("EraseClipCursor:\n");
//	(*pClipCursor)(NULL);
//}

//void GameWindowInfo::SethWnd(HWND hwnd) 
//{
//	//OutTraceD("SethWnd: setting main win=%x\n", hwnd);
//	hWnd=hwnd; 
//	//hWndFPS=hwnd;
//}

void GameWindowInfo::FixWorkarea(LPRECT workarea)
{
	int w, h, b; // width, height and border
	int iRatioX, iRatioY;

	// ratio is the configured one, unless the values are 0. Then, it's the standard 4:3 ratio.
	iRatioX = iSizX ? iSizX : 4;
	iRatioY = iSizY ? iSizY : 3;

	w = workarea->right - workarea->left;
	h = workarea->bottom - workarea->top;
	if ((w * iRatioY) > (h * iRatioX)){
		b = (w - (h * iRatioX / iRatioY))/2;
		workarea->left += b;
		workarea->right -= b;
	}
	else {
		b = (h - (w * iRatioY / iRatioX))/2;
		workarea->top += b;
		workarea->bottom -= b;
	}
}

//RECT GameWindowInfo::MapWindowRect(LPRECT lpRect)
//{
//	POINT UpLeft={0,0};
//	RECT RetRect;
//	RECT ClientRect;
//	int w, h, bx, by; // width, height and x,y borders
//	int iRatioX, iRatioY;
//
//	// ratio is the configured one, unless the values are 0. Then, it's the standard 4:3 ratio.
//	iRatioX = iSizX ? iSizX : 4;
//	iRatioY = iSizY ? iSizY : 3;
//
//	if (!GetClientRect(hWnd, &ClientRect)){
//		//OutTraceE("GetClientRect ERROR: err=%d hwnd=%x at %d\n", GetLastError(), hWnd, __LINE__);
//		// v2.02.31: try....
//		ClientRect.top=ClientRect.left=0;
//		ClientRect.right=iRatioX;
//		ClientRect.bottom=iRatioY;
//	}
//	
//	RetRect=ClientRect;
//	bx = by = 0;
//	//if (dwFlags2 & KEEPASPECTRATIO){
//		w = RetRect.right - RetRect.left;
//		h = RetRect.bottom - RetRect.top;
//		if ((w * iRatioY) > (h * iRatioX)){
//			bx = (w - (h * iRatioX / iRatioY))/2;
//		}
//		else {
//			by = (h - (w * iRatioY / iRatioX))/2;
//		}
//	//}
//
//	if(lpRect){ // v2.02.41 - fixed coordinates for KEEPASPECTRATIO option
//		LONG Width, Height;
//		Width = ClientRect.right - (2*bx);
//		Height = ClientRect.bottom - (2*by);
//		RetRect.left = bx + (lpRect->left * Width / dwScreenWidth);
//		RetRect.right = bx + (lpRect->right * Width / dwScreenWidth);
//		RetRect.top = by + (lpRect->top * Height / dwScreenHeight);
//		RetRect.bottom = by + (lpRect->bottom * Height / dwScreenHeight);
//	}
//	else{
//		RetRect.left = ClientRect.left + bx;
//		RetRect.right = ClientRect.right - bx;
//		RetRect.top = ClientRect.top + by;
//		RetRect.bottom = ClientRect.bottom - by;
//	}
//
//	// TODO: Some kind of actual error management here? Or should this "never" happen?
//	if(!ClientToScreen(hWnd, &UpLeft)){
//		//OutTraceE("ClientToScreen ERROR: err=%d hwnd=%x at %d\n", GetLastError(), hWnd, __LINE__);
//	}
//	if(!OffsetRect(&RetRect ,UpLeft.x, UpLeft.y)){
//		//OutTraceE("OffsetRect ERROR: err=%d hwnd=%x at %d\n", GetLastError(), hWnd, __LINE__);
//	}
//	return RetRect;
//}

POINT GameWindowInfo::FixMessagePt(HWND hwnd, POINT point)
{
	RECT rect;
	static POINT curr;
	curr=point;

	if(!(*pScreenToClient)(hwnd, &curr)){
		//OutTraceE("ScreenToClient ERROR=%d hwnd=%x at %d\n", GetLastError(), hwnd, __LINE__);
		curr.x = curr.y = 0;
	}

	if (!(*pGetClientRect)(hwnd, &rect)) {
		//OutTraceE("GetClientRect ERROR=%d hwnd=%x at %d\n", GetLastError(), hwnd, __LINE__);
		curr.x = curr.y = 0;
	}

//#ifdef ISDEBUG
//	if(IsDebug) OutTrace("FixMessagePt point=(%d,%d) hwnd=%x win pos=(%d,%d) size=(%d,%d)\n",
//		point.x, point.y, hwnd, point.x-curr.x, point.y-curr.y, rect.right, rect.bottom);
//#endif

	if (curr.x < 0) curr.x=0;
	if (curr.y < 0) curr.y=0;
	if (curr.x > rect.right) curr.x=rect.right;
	if (curr.y > rect.bottom) curr.y=rect.bottom;
	if (rect.right)  curr.x = (curr.x * gwi.GetScreenWidth()) / rect.right;
	if (rect.bottom) curr.y = (curr.y * gwi.GetScreenHeight()) / rect.bottom;

	return curr;
}


void GameWindowInfo::MapToWindow(LPRECT rect)
{
	POINT upleft = { 0, 0 };
	ClientToScreen(hWnd, &upleft);
	rect->left += upleft.x;
	rect->top += upleft.y;
	rect->right += upleft.x;
	rect->bottom += upleft.y;
}

void GameWindowInfo::MapToWindow(int *nLeftDest, int *nTopDest, int *nRightDest, int *nBottomDest)
{
	POINT upleft = { 0, 0 };
	ClientToScreen(hWnd, &upleft);
	*nLeftDest += upleft.x;
	*nTopDest += upleft.y;
	*nRightDest += upleft.x;
	*nBottomDest += upleft.y;
}

void GameWindowInfo::MapToWindow(LPPOINT lppoint)
{
	POINT upleft = { 0, 0 };
	ClientToScreen(hWnd, &upleft);
	lppoint->x += upleft.x;
	lppoint->y += upleft.y;
}

void GameWindowInfo::MapToWindow(int *nXDest, int *nYDest)
{
	POINT upleft = { 0, 0 };
	ClientToScreen(hWnd, &upleft);
	*nXDest += upleft.x;
	*nYDest += upleft.y;
}

void GameWindowInfo::UnmapFromWindow(LPPOINT lppoint)
{
	POINT upleft = { 0, 0 };
	ClientToScreen(hWnd, &upleft);
	lppoint->x -= upleft.x;
	lppoint->y -= upleft.y;
}

void GameWindowInfo::UnmapFromWindow(int *nXDest, int *nYDest)
{
	POINT upleft = { 0, 0 };
	ClientToScreen(hWnd, &upleft);
	*nXDest -= upleft.x;
	*nYDest -= upleft.y;
}

POINT GameWindowInfo::ClientOffset(HWND hwnd)
{
	RECT desktop;
	POINT upleft, win0, desk0, ret;
	ret.x = ret.y = 0;

	GetClientRect(hWnd, &desktop);
	if(!desktop.right || !desktop.bottom) return ret;

	upleft.x = upleft.y = 0;
	ClientToScreen(hwnd, &upleft);
	win0 = upleft;
	upleft.x = upleft.y = 0;
	ClientToScreen(hWnd, &upleft);
	desk0 = upleft;
	if (desktop.right) ret.x = ((win0.x - desk0.x) * (LONG)dwScreenWidth) / desktop.right;
	if (desktop.bottom) ret.y = ((win0.y - desk0.y) * (LONG)dwScreenHeight) / desktop.bottom;
	//OutTraceB("ClientOffset: hwnd=%x offset=(%d,%d)\n", hwnd, ret.x, ret.y);
	return ret;
}

//RECT GameWindowInfo::GetWindowRect(RECT win)
//{
//	RECT desktop;
//	POINT desk0;
//	desk0.x = desk0.y = 0;
//
//	(*pGetClientRect)(hWnd, &desktop);
//	(*pClientToScreen)(hWnd,&desk0);
//
//	if(!desktop.right || !desktop.bottom) return win;
//
//	win.left = ((win.left - desk0.x) * (LONG)dwScreenWidth) / desktop.right;
//	win.top = ((win.top - desk0.y) * (LONG)dwScreenHeight) / desktop.bottom;
//	win.right = ((win.right - desk0.x) * (LONG)dwScreenWidth) / desktop.right;
//	win.bottom = ((win.bottom - desk0.y) * (LONG)dwScreenWidth) / desktop.right;
//
//	return win;
//}

//RECT GameWindowInfo::GetClientRect(RECT win)
//{
//	RECT desktop;
//	(*pGetClientRect)(hWnd, &desktop);
//
//	if(!desktop.right || !desktop.bottom) return win;
//
//	win.left = (win.left * dwScreenWidth) / desktop.right;
//	win.top = (win.top * dwScreenHeight) / desktop.bottom;
//	win.right = (win.right * dwScreenWidth) / desktop.right;
//	win.bottom = (win.bottom * dwScreenHeight) / desktop.bottom;
//
//	return win;
//}

POINT GameWindowInfo::AddCoordinates(POINT p1, POINT p2)
{
	POINT ps;
	ps.x = p1.x + p2.x;
	ps.y = p1.y + p2.y;
	return ps;
}

POINT GameWindowInfo::SubCoordinates(POINT p1, POINT p2)
{
	POINT ps;
	ps.x = p1.x - p2.x;
	ps.y = p1.y - p2.y;
	return ps;
}

void GameWindowInfo::ScreenRefresh(void)
{
	// optimization: don't blit too often!
	// 20mSec seems a good compromise.
	#define DXWREFRESHINTERVAL 20

	LPDIRECTDRAWSURFACE lpDDSPrim;
	extern HRESULT WINAPI extBlt(LPDIRECTDRAWSURFACE lpdds, LPRECT lpdestrect, LPDIRECTDRAWSURFACE lpddssrc, LPRECT lpsrcrect, DWORD dwflags, LPDDBLTFX lpddbltfx);

	static int t = -1;
	if (t == -1)
		t = (*pGetTickCount)()-(DXWREFRESHINTERVAL+1); // V.2.1.69: trick - subtract 
	int tn = (*pGetTickCount)();

	if (tn-t < DXWREFRESHINTERVAL) return;
	t = tn;

	// if not too early, refresh colors on primary surface ....
	lpDDSPrim=gwi.GetPrimarySurface();
	if (lpDDSPrim) extBlt(lpDDSPrim, NULL, lpDDSPrim, NULL, 0, NULL);

	// v2.02.44 - used for what? Commenting out seems to fix the palette update glitches  
	// and make the "Palette updates don't blit"option useless....
	//(*pInvalidateRect)(hWnd, NULL, FALSE); 
}

//void GameWindowInfo::DoSlow(int delay)
//{
//	MSG uMsg;
//	int t, tn;
//	t = (*pGetTickCount)();
//
//	uMsg.message=0; // initialize somehow...
//	while((tn = (*pGetTickCount)())-t < delay){
//		while (PeekMessage (&uMsg, NULL, 0, 0, PM_REMOVE) > 0){
//			if(WM_QUIT == uMsg.message) break;
//			TranslateMessage (&uMsg);
//			DispatchMessage (&uMsg);
//		}	
//		(*pSleep)(1);
//	}
//}
/*
static void CountFPS(HWND hwnd)
{
	static DWORD time = 0xFFFFFFFF;
	static DWORD FPSCount = 0;
	extern void SetFPS(int);
	//DXWNDSTATUS Status;
	DWORD tmp;
	tmp = (*pGetTickCount)();
	if((tmp - time) > 1000) {
		char sBuf[80+15+1]; // title + fps string + terminator
		char *fpss;
		// log fps count
		OutTrace("FPS: Delta=%x FPSCount=%d\n", (tmp-time), FPSCount);
		// show fps count on status win
		GetHookInfo()->FPSCount = FPSCount; // for overlay display
		// show fps on win title bar
		if (gwi.dwFlags2 & SHOWFPS){
			GetWindowText(hwnd, sBuf, 80);
			fpss=strstr(sBuf," ~ (");
			if(fpss==NULL) fpss=&sBuf[strlen(sBuf)];
			sprintf_s(fpss, 15, " ~ (%d FPS)", FPSCount);
			SetWindowText(hwnd, sBuf);
		}
		// reset
		FPSCount=0;
		time = tmp;
	}
	else {
		FPSCount++;
		OutTraceB("FPS: Delta=%x FPSCount++=%d\n", (tmp-time), FPSCount);
	}
}
*/
static void LimitFrameCount(DWORD delay)
{
	static DWORD oldtime=(*pGetTickCount)();
	DWORD newtime;
	newtime = (*pGetTickCount)();
	// use '<' and not '<=' to avoid the risk of sleeping forever....
	if(newtime < oldtime+delay) {
		//if(IsDebug) OutTrace("FPS limit: old=%x new=%x delay=%d sleep=%d\n", 
		//	oldtime, newtime, delay, (oldtime+delay-newtime));
		(*pSleep)(oldtime+delay-newtime);
		// no good processing messages in the meanwhile: AoE series won't work at all!
		// don't use DoSlow(oldtime+delay-newtime); 
		oldtime += delay; // same as doing "oldtime=(*pGetTickCount)();" now
	}
	else
		oldtime = newtime;
}

static BOOL SkipFrameCount(DWORD delay)
{
	static DWORD oldtime=(*pGetTickCount)();
	DWORD newtime;
	newtime = (*pGetTickCount)();
	if(newtime < oldtime+delay) return TRUE; // TRUE => skip the screen refresh
	oldtime = newtime;
	return FALSE; // don't skip, do the update

}

//BOOL GameWindowInfo::HandleFPS()
//{
//	//if(dwFlags2 & (SHOWFPS|SHOWFPSOVERLAY)) CountFPS(hWndFPS);
//	if(dwFlags2 & LIMITFPS) LimitFrameCount(gwi.MaxFPS);
//	if(dwFlags2 & SKIPFPS) if(SkipFrameCount(gwi.MaxFPS)) return TRUE;
//	return FALSE;
//}

static DWORD TimeShifter(DWORD val, int shift)
{
	int exp, reminder;
	if (shift > 0) {
		exp = shift >> 1;
		reminder = shift & 0x1;
		if (reminder) val -= (val >> 2); // val * (1-1/4) = val * 3/4
		val >>= exp; // val * 2^exp
	}
	if (shift < 0) {
		exp = (-shift) >> 1;
		reminder = (-shift) & 0x1;
		val <<= exp; // val / 2^exp
		if (reminder) val += (val >> 1); // val * (1+1/2) = val * 3/2
	}
	return val;
}

//DWORD GameWindowInfo::GetTickCount(void)
//{
//	DWORD dwTick;
//	static DWORD dwLastRealTick=0;
//	static DWORD dwLastFakeTick=0;
//	DWORD dwNextRealTick;
//
//	dwNextRealTick=(*pGetTickCount)();
//	dwTick=(dwNextRealTick-dwLastRealTick);
//	TimeShift=GetHookInfo()->TimeShift;
//	dwTick = TimeShifter(dwTick, TimeShift);
//	dwLastFakeTick += dwTick;
//	dwLastRealTick = dwNextRealTick;
//	return dwLastFakeTick;
//}

//DWORD GameWindowInfo::StretchTime(DWORD dwTimer)
//{
//	TimeShift=GetHookInfo()->TimeShift;
//	dwTimer = TimeShifter(dwTimer, -TimeShift);
//	return dwTimer;
//}

//DWORD GameWindowInfo::StretchCounter(DWORD dwTimer)
//{
//	TimeShift=GetHookInfo()->TimeShift;
//	dwTimer = TimeShifter(dwTimer, TimeShift);
//	return dwTimer;
//}

//void GameWindowInfo::GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime)
//{
//	DWORD dwTick;
//	DWORD dwCurrentTick;
//	FILETIME CurrFileTime;
//	static DWORD dwStartTick=0;
//	static DWORD dwUpdateTick=0;
//	static FILETIME StartFileTime;
//	extern DXWNDSTATUS *pStatus;
//
//	if(dwStartTick==0) {
//		SYSTEMTIME StartingTime;
//		// first time through, initialize & return true time
//		dwStartTick = (*pGetTickCount)();
//		(*pGetSystemTime)(&StartingTime);
//		SystemTimeToFileTime(&StartingTime, &StartFileTime);
//		*lpSystemTimeAsFileTime = StartFileTime;
//	}
//	else {
//		dwCurrentTick=(*pGetTickCount)();
//		dwTick=(dwCurrentTick-dwStartTick);
//		TimeShift=GetHookInfo()->TimeShift;
//		dwTick = TimeShifter(dwTick, TimeShift);
//		// From MSDN: Contains a 64-bit value representing the number of 
//		// 100-nanosecond intervals since January 1, 1601 (UTC).
//		// So, since 1mSec = 10.000 * 100nSec, you still have to multiply by 10.000.
//		CurrFileTime.dwHighDateTime = StartFileTime.dwHighDateTime; // wrong !!!!
//		CurrFileTime.dwLowDateTime = StartFileTime.dwLowDateTime + (10000 * dwTick); // wrong !!!!
//		*lpSystemTimeAsFileTime=CurrFileTime;
//		// reset to avoid time jumps on TimeShift changes...
//		StartFileTime = CurrFileTime;
//		dwStartTick = dwCurrentTick;
//	}
//}

//TODO: Make this return OUR SystemTime.
//void GameWindowInfo::GetSystemTime(LPSYSTEMTIME lpSystemTime)
//{
//	DWORD dwTick;
//	DWORD dwCurrentTick;
//	FILETIME CurrFileTime;
//	static DWORD dwStartTick=0;
//	static DWORD dwUpdateTick=0;
//	static FILETIME StartFileTime;
//	extern DXWNDSTATUS *pStatus;
//
//	if(dwStartTick==0) {
//		SYSTEMTIME StartingTime;
//		// first time through, initialize & return true time
//		dwStartTick = (*pGetTickCount)();
//		(*pGetSystemTime)(&StartingTime);
//		SystemTimeToFileTime(&StartingTime, &StartFileTime);
//		*lpSystemTime = StartingTime;
//	}
//	else {
//		dwCurrentTick=(*pGetTickCount)();
//		dwTick=(dwCurrentTick-dwStartTick);
//		TimeShift=GetHookInfo()->TimeShift;
//		dwTick = TimeShifter(dwTick, TimeShift);
//		// From MSDN: Contains a 64-bit value representing the number of 
//		// 100-nanosecond intervals since January 1, 1601 (UTC).
//		// So, since 1mSec = 10.000 * 100nSec, you still have to multiply by 10.000.
//		CurrFileTime.dwHighDateTime = StartFileTime.dwHighDateTime; // wrong !!!!
//		CurrFileTime.dwLowDateTime = StartFileTime.dwLowDateTime + (10000 * dwTick); // wrong !!!!
//		FileTimeToSystemTime(&CurrFileTime, lpSystemTime);
//		// reset to avoid time jumps on TimeShift changes...
//		StartFileTime = CurrFileTime;
//		dwStartTick = dwCurrentTick;
//	}
//}


//char *GameWindowInfo::GetTSCaption(int shift)
//{
//	static char *sTSCaption[17]={
//		"x16","x12","x8","x6",
//		"x4","x3","x2","x1.5",
//		"x1",
//		":1.5",":2",":3",":4",
//		":6",":8",":12",":16"};
//	if (shift<(-8) || shift>(+8)) return "???";
//	return sTSCaption[shift+8];
//}

//char *GameWindowInfo::GetTSCaption(void)
//{
//	return GetTSCaption(TimeShift);
//}

/*
int GameWindowInfo::GetDLLIndex(char *lpFileName)
{
	int idx;
	char *lpName, *lpNext;
	char *SysNames[]={
		"kernel32",
		"USER32",
		"GDI32",
		"imelib",
		"ADVAPI32",
		"ole32",
		"ddraw",
		"d3d8",
		"d3d9",
		"d3d10",
		"d3d10_1",
		"d3d11",
		"opengl32",
 		"msvfw32",
		"smackw32",
		"version",
		"dplayx",
		"dsound",
		"winmm",
		"imm32",
		"wsock32",
		"dinput",
		"dinput8",
		"shfolder",
		"shell32",
		"ws2_32",
		"tapi32",
		"netapi32",
		"wintrust",
		"advapi32",
		"d3dim",
		"d3dim700",
		NULL
	};	
	
	lpName=lpFileName;
	while (lpNext=strchr(lpName,'\\')) lpName=lpNext+1;
	for(idx=0; SysNames[idx]; idx++){
		char SysNameExt[81];
		strcpy(SysNameExt, SysNames[idx]);
		strcat(SysNameExt, ".dll");
		if(
			(!lstrcmpi(lpName,SysNames[idx])) ||
			(!lstrcmpi(lpName,SysNameExt))
		){
			//OutTraceB("Registered DLL FileName=%s\n", lpFileName);
			break;
		}
	}
	if (!SysNames[idx]) return -1;
	return idx;
}*/
/*
void GameWindowInfo::FixStyle(char *ApiName, HWND hwnd, WPARAM wParam, LPARAM lParam)
{
	LPSTYLESTRUCT lpSS;
	lpSS = (LPSTYLESTRUCT) lParam;

	switch (wParam) {
	case GWL_STYLE:
		OutTraceD("%s: new Style=%x(%s)\n", 
			ApiName, lpSS->styleNew, ExplainStyle(lpSS->styleNew));
		if (gwi.dwFlags1 & FIXWINFRAME){ // set canonical style
			lpSS->styleNew= WS_OVERLAPPEDWINDOW;
		}
		if (gwi.dwFlags1 & LOCKWINSTYLE){ // set to current value
			lpSS->styleNew= (*pGetWindowLong)(hwnd, GWL_STYLE);
		}
		if (gwi.dwFlags1 & PREVENTMAXIMIZE){ // disable maximize settings
			if (lpSS->styleNew & WS_MAXIMIZE){
				OutTraceD("%s: prevent maximize style\n", ApiName);
				lpSS->styleNew &= ~WS_MAXIMIZE;
			}
		}
		break;
	case GWL_EXSTYLE:
		OutTraceD("%s: new ExStyle=%x(%s)\n", 
			ApiName, lpSS->styleNew, ExplainExStyle(lpSS->styleNew));
		if (gwi.dwFlags1 & FIXWINFRAME){ // set canonical style
			lpSS->styleNew= 0;
		}
		if (gwi.dwFlags1 & LOCKWINSTYLE){ // set to current value
				lpSS->styleNew= (*pGetWindowLong)(hwnd, GWL_EXSTYLE);
		}
		if ((gwi.dwFlags1 & PREVENTMAXIMIZE) && (hwnd==hWnd)){ // disable maximize settings
			if (lpSS->styleNew & WS_EX_TOPMOST){
				OutTraceD("%s: prevent EXSTYLE topmost style\n", ApiName);
				lpSS->styleNew &= ~WS_EX_TOPMOST;
			}
		}
		break;
	default:
		break;
	}
}
*/

// TODO: Proxy all emulated DCs to become DC Ex, might be necessary to handle more advanced games...
HDC GameWindowInfo::AcquireEmulatedDC(HWND hwnd)
{
	HDC wdc;
	//if(!(wdc=(*pGDIGetDC)(hwnd)))
	GetDC(hwnd);
	//OutTraceE("GetDC: ERROR err=%d at=%d\n", GetLastError(), __LINE__);
	return AcquireEmulatedDC(wdc);
}

HDC GameWindowInfo::AcquireEmulatedDC(HDC wdc)
{
	if(!VirtualHDC) // No VirtualHDC defined yet, or it's been invalidated.
	{
		VirtualHDC = CreateCompatibleDC(wdc);
		if(!VirtualPic)
		{
			VirtualPic = CreateCompatibleBitmap(wdc, gwi.dwScreenWidth, gwi.dwScreenHeight);
		}
		if(!SelectObject(VirtualHDC, VirtualPic))
		{
			DeleteObject(VirtualPic);
			VirtualPic = CreateCompatibleBitmap(wdc, gwi.dwScreenWidth, gwi.dwScreenHeight);
			SelectObject(VirtualHDC, VirtualPic);
		}
	}
	return VirtualHDC;
}

BOOL GameWindowInfo::ReleaseEmulatedDC(HWND hwnd)
{
	//BOOL ret;
	HDC wdc;
	RECT client;
	GetClientRect(hwnd, &client);
	//if(!(wdc=GDIGetDC)(hwnd)))
		//OutTraceE("GetDC: ERROR err=%d at=%d\n", GetLastError(), __LINE__);
	wdc = GetDC(hwnd);
	//if(!(*pGDIStretchBlt)(wdc, 0, 0, client.right, client.bottom, VirtualHDC, 0, 0, gwi.GetScreenWidth(), gwi.GetScreenHeight(), SRCCOPY))
		//OutTraceE("StretchBlt: ERROR err=%d at=%d\n", GetLastError(), __LINE__);
	BitBlt(wdc, 0, 0, client.right, client.bottom, VirtualHDC, 0, 0, SRCCOPY);
	InvalidateRect(hwnd, NULL, 0);
	//ret = TRUE;
	//return ret;
	return TRUE;
}

void GameWindowInfo::ResetEmulatedDC()
{
	VirtualHDC = NULL;
	VirtualPic = NULL;
	VirtualOffsetX = 0;
	VirtualOffsetY = 0;
}

BOOL GameWindowInfo::IsVirtual(HDC hdc)
{
	return (hdc == VirtualHDC) && bEmulateDC;
}

void GameWindowInfo::SetPalette(DWORD dwStart, DWORD dwCount, LPPALETTEENTRY lppalPalette)
{
	for(DWORD i = 0; i < dwCount; i++)
		palPalette[dwStart+i] = lppalPalette[i];

	for(DWORD i = 0; i < dwCount; i++)
	{
		PALETTEENTRY PalColor;
		PalColor = lppalPalette[i];

		switch(ddpfActualPixelFormat.dwRGBBitCount)
		{
			case 32:
				dwPaletteEntries[i + dwStart] =
					(((DWORD)PalColor.peRed) << 16) + (((DWORD)PalColor.peGreen) << 8) + ((DWORD)PalColor.peBlue);
				break;
			case 16:
				dwPaletteEntries[i + dwStart] = (ddpfActualPixelFormat.dwGBitMask == 0x03E0) ?
					(((DWORD)PalColor.peRed & 0xF8) << 8) + (((DWORD)PalColor.peGreen & 0xFC) << 3) + (((DWORD)PalColor.peBlue &0xF8) >> 3) :
					(((DWORD)PalColor.peRed & 0xF8) << 8) + (((DWORD)PalColor.peGreen & 0xF8) << 3) + (((DWORD)PalColor.peBlue &0xF8) >> 3);
				break;
			default:
				break;
		}
	}

	bIsPaletteUpdated = true;
}

// TODO: Can this be compacted?
COLORREF GameWindowInfo::GetMatchingColor(COLORREF crColor)
{
	int iDistance, iMinDistance;
	int iColorIndex, iMinColorIndex;
	COLORREF PalColor;

	iMinDistance = 0xFFFFFF;
	iMinColorIndex = 0;

	for(iColorIndex = 0; iColorIndex < 256; iColorIndex++)
	{
		int iDist;
		iDistance = 0;

		PalColor = dwPaletteEntries[iColorIndex];
		switch(ActualPixelFormat.dwRGBBitCount)
		{
			case 32:
				PalColor = ((PalColor & 0x00FF0000) >> 16) | (PalColor & 0x0000FF00) | ((PalColor & 0x000000FF) << 16);
				break;
			case 16:
				if(ActualPixelFormat.dwGBitMask == 0x03E0)
				{
					// RGB555 screen settings
					PalColor = ((PalColor & 0x7C00) >> 7) | ((PalColor & 0x03E0) << 6) | ((PalColor & 0x001F) << 19);
				}
				else
				{
					// RGB565 screen settings
					PalColor = ((PalColor & 0xF800) >> 8) | ((PalColor & 0x07E0) << 5) | ((PalColor & 0x001F) << 19);
				}
				break;
		}

		iDist = (crColor & 0x00FF0000) - (PalColor & 0x00FF0000);
		iDist >>= 16;
		if(iDist < 0) iDist = -iDist;
		iDist *= iDist;
		iDistance += iDist;

		iDist = (crColor & 0x0000FF00) - (PalColor & 0x0000FF00);
		iDist >>= 8;
		if(iDist < 0) iDist = -iDist;
		iDist *= iDist;
		iDistance += iDist;

		iDist = (crColor & 0x000000FF) - (PalColor & 0x000000FF);
		// iDist >>= 0;
		if(iDist < 0) iDist =- iDist;
		iDist *= iDist;
		iDistance += iDist;

		if(iDistance < iMinDistance)
		{
			iMinDistance = iDistance;
			iMinColorIndex = iColorIndex;
		}

		if(iMinDistance == 0) break; // got the perfect match!
	}
	
	PalColor = dwPaletteEntries[iMinColorIndex];
	switch(ActualPixelFormat.dwRGBBitCount)
	{
		case 32:
			crColor = ((PalColor & 0x00FF0000) >> 16) | (PalColor & 0x0000FF00) | ((PalColor & 0x000000FF) << 16); 
			break;
		case 16:
			if(ActualPixelFormat.dwGBitMask == 0x03E0)
			{
				// RGB555 screen settings
				crColor = ((PalColor & 0x7C00) >> 7) | ((PalColor & 0x03E0) << 6) | ((PalColor & 0x001F) << 19);
			}
			else
			{
				// RGB565 screen settings
				crColor = ((PalColor & 0xF800) >> 8) | ((PalColor & 0x07E0) << 5) | ((PalColor & 0x001F) << 19);
			}
			break;
	}
	return crColor;
}

GameWindowInfo gwi;

#endif